#ifdef ENABLE_MAPLE_SAN

#include "asan_phases.h"
#include "asan_function.h"
#include "asan_razor.h"
#include "asan_module.h"
#include "san_common.h"
#include "me_cfg.h"
#include "mempool.h"
#include "asan_config.h"
#include "asan_asap.h"


namespace maple {

void MEDoAsan::GetAnalysisDependence(maple::AnalysisDep &aDep) const {
    aDep.AddRequired<MEDoVarCheck>();

    if (ENABLE_ASAN_NEIGHBOR_OPT) {
      aDep.AddRequired<MEDominance>();
      aDep.AddRequired<MEAliasClass>();
      aDep.AddRequired<MESSA>();
      // TODO: we need to filter results of  MEDoVarCheck
      aDep.AddRequired<MEDoNeighborOpt>();
      aDep.AddRequired<MEIRMapBuild>();  // to me ir
      aDep.AddRequired<MEPreMeEmission>();  // to maple ir
    }
    aDep.SetPreservedAll();
}

bool MEDoAsan::PhaseRun(maple::MeFunction &f) {
  // The reture value is said to show whether this phase modifies IR
  // The document said the return value is not used
  PreAnalysis *symbol_interesting = GET_ANALYSIS(MEDoVarCheck, f);
  
  std::vector<AsanNodePos> targetPos;
  if (ENABLE_ASAN_NEIGHBOR_OPT)
    targetPos = GET_ANALYSIS(MEDoNeighborOpt, f);
  if (symbol_interesting == nullptr) {
    LogInfo::MapleLogger() << "The MEDoVarCheck::PhaseRun is not called " << f.GetName() << "\n";
  }
  LogInfo::MapleLogger() << "The MEDoAsan::PhaseRun is running " << f.GetName() << "\n";
  AddressSanitizer Asan(f.GetMIRModule(), symbol_interesting, targetPos);
  Asan.instrumentFunction(f);
  return true;
}

void MEDoVarCheck::GetAnalysisDependence(maple::AnalysisDep &aDep) const {
    aDep.SetPreservedAll();
}

bool MEDoVarCheck::PhaseRun(maple::MeFunction &f) {
  LogInfo::MapleLogger() << "The MEDoVarCheck::PhaseRun is running " << f.GetName() << "\n";
  MemPool *memPool = GetPhaseMemPool();
  PreAnalysis *preAnalysis = memPool->New<PreAnalysis>(*memPool);

  MIRFunction *mirFunction = f.GetMirFunc();
  std::set<MIRSymbol*> addrOfSymList;

  std::stack<BaseNode*> baseNodeStack;
  for (StmtNode &stmt : mirFunction->GetBody()->GetStmtNodes()) {
    baseNodeStack.push(&stmt);
  }

  while (!baseNodeStack.empty()) {
    BaseNode *baseNode = baseNodeStack.top();
    baseNodeStack.pop();
    if (baseNode->GetOpCode() == OP_addrof) {
      AddrofNode *addrofNode = dynamic_cast<AddrofNode *>(baseNode);
      MIRSymbol *mirSymbol = mirFunction->GetLocalOrGlobalSymbol(addrofNode->GetStIdx());
      addrOfSymList.insert(mirSymbol);
    }
    else if (baseNode->GetOpCode() == OP_block) {
      // Actually, this phase runs before other phases
      // This block may never execute
      // We add this here to ensure all stmtNodes are visited
      BlockNode* tmpBlock = dynamic_cast<BlockNode*>(baseNode);
      CHECK_NULL_FATAL(tmpBlock);
      for (StmtNode &stmt : tmpBlock->GetStmtNodes()) {
        baseNodeStack.push(&stmt);
      }
    }
    for (size_t j = 0; j < baseNode->NumOpnds(); j++) {
      baseNodeStack.push(baseNode->Opnd(j));
    }
  }

  MIRSymbolTable *symbolTable = mirFunction->GetSymTab();
  size_t size = symbolTable->GetSymbolTableSize();
  // LabelIdx is the type of uint32_t, we should have a LabelIdx_MAX
  CHECK_FATAL(size < UINT32_MAX, "Too large table size");
  for (uint32_t i = 0; i < uint32_t(size); ++i) {
    MIRSymbol *symbol = symbolTable->GetSymbolFromStIdx(LabelIdx(i));
    if (symbol == nullptr) {
      continue;
    }
    if (symbol->IsDeleted() || symbol->GetName() == "") {
      continue;
    }

    for (MIRSymbol *mirSymbol : addrOfSymList) {
      if (mirSymbol->GetStIdx() == symbol->GetStIdx()) {
        preAnalysis->usedInAddrof.push_back(symbol);
      }
    }
  }
  this->result = preAnalysis;
  LogInfo::MapleLogger() << "The MEDoVarCheck::PhaseRun ends " << f.GetName() << "\n";
  return true;
}

PreAnalysis* MEDoVarCheck::GetResult() {
    return this->result;
}

void MEASanRazor::GetAnalysisDependence(maple::AnalysisDep &aDep) const {
    // aDep.AddRequired<MEDoAsan>();
    aDep.SetPreservedAll();
}

bool MEASanRazor::PhaseRun(maple::MeFunction &f) {
  ASanRazor razor;
  bool changed = razor.InstrumentFunctionForProfiling(f, *AsanBlockListPtr);
  if (SanRazorIs2ndCompile) {
    razor.ClearSanRazorCoverageStmtNodes();
    if (ASAN_DEBUG_MODE) {
      LogInfo::MapleLogger() << "ASAP Process function: " << f.GetName() << "\n";
    }
    razor.RemoveRedundantInstrumentation(f, *AsanBlockListPtr);
    ASAPRemoveAsanChecks(AsanModulePtr->GetSrcFilePath(), f.GetMirFunc()->GetBody());
  }
  return changed;
}

}  // namespace maple

#endif
